/*
 * This file is part of Ember.
 *
 * Ember is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Ember is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Ember.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Copyright (C) Grant Tyra 2010
 * granttyra@gmail.com
*/

#include "ember_native.h"
#include "ember_call_stack.h"
#include "ember_types.h"
#include "ember_string.h"
#include <stdio.h>

#ifdef WIN32
#include <windows.h>
#else
#include <dlfcn.h>
#include <unistd.h>
#endif

EmberDictionary *loaded_modules = NULL;

void *load_lib(const char *module_name) {
#ifdef WIN32
	HINSTANCE extension_lib = LoadLibrary(module_name);
	EmberDictionaryPut(loaded_modules, module_name, extension_lib);
	return extension_lib;
#else
	void *extension_lib = dlopen(module_name, RTLD_NOW);
	char *error = dlerror();

	if (error != NULL) {
		printf("DLERROR: %s\n", error);
	}

    EmberDictionaryPut(loaded_modules, module_name, extension_lib);
    return extension_lib;
#endif
}

void *get_function_ptr(const char *module_name, const char *function_name) {
#ifdef WIN32
	char *module_file = EmberStringConcat(module_name, "_emb.dll");
#else
    char *module_file = EmberStringConcat(module_name, "_emb.so");
#endif
	void *module_ptr = EmberDictionaryGet(loaded_modules, module_file);

	if (module_ptr == NULL) {
		module_ptr = load_lib(module_file);
	}
    
#ifdef WIN32
	return GetProcAddress(module_ptr, function_name);
#else
    return dlsym(module_ptr, function_name);
#endif
}


EmberObject *ember_native_call() {
	EmberObject *module_name = EmberCallStackRetrieve("module_name");
	EmberObject *function_name = EmberCallStackRetrieve("function_name");
	EmberObject *argdict = EmberCallStackRetrieve("argdict");
	EmberObject *(*function)() = get_function_ptr(EmberStringValue(module_name), EmberStringValue(function_name));
	EmberObject *returnval = NULL;
	EmberStackFrame *frame = NULL;

	if (function == NULL) {
		return EmberObjectCreateException(EmberStringConcat("Unable to locate native method: ", EmberStringValue(function_name)));
	}

	frame = EmberStackFrameCreate();
	EmberStackFrameBind(frame, "argdict", argdict);
	EmberCallStackPush(frame);

	returnval = (*function)();

	EmberCallStackPop();
	EmberStackFrameDestroy(frame);
	return returnval;
}		

void ember_native_install() {
	EmberClassDef *classdef = EmberClassCreate("Native", NULL);
	EmberDictionaryPut(classdef->classMethods, "call", EmberMethodCreate("module_name, function_name, argdict", 1, ember_native_call));
	EmberClassMapPut(classdef);
	loaded_modules = EmberDictionaryCreate();
}
